﻿#region [ License information          ]
/* ************************************************************
 *
 * Copyright (c) Bjørn Bouet Smith, 2010, 2011
 *
 * This source code is subject to terms and conditions of 
 * Microsoft Public License (Ms-PL).
 * 
 * A copy of the license can be found in the license.txt
 * file at the root of this distribution. If you can not 
 * locate the License, please send an email to bjornsmith@gmail.com
 * 
 * By using this source code in any fashion, you are 
 * agreeing to be bound by the terms of the Microsoft 
 * Public License.
 *
 * You must not remove this notice, or any other, from this
 * software.
 *
 * ************************************************************/
#endregion

using System;
using System.Collections.Generic;
using System.Text;

namespace Smith.AMC.Operations
{
    /// <summary>
    /// Represents an operation that can be done against a memcached server.
    /// </summary>
    public abstract class Operation
    {
        /// <summary>
        /// the space character
        /// </summary>
        protected const string Space = " ";

        /// <summary>
        /// The byte array representing a line break
        /// </summary>
        private static readonly byte[] CrLf = new[]
                       {
                           (byte) '\r',
                           (byte) '\n'
                       };

        /// <summary>
        /// Initializes a new instance of the <see cref="Operation"/> class.
        /// </summary>
        /// <param name="operationName">Name of the operation.</param>
        protected Operation(string operationName)
        {
            Name = operationName;
        }

        /// <summary>
        /// Gets the operation name.
        /// </summary>
        public string Name
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the cache key.
        /// </summary>
        public string Key
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the expiry for the given operation
        /// </summary>
        public TimeSpan Expiry
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags that will be sent along with the operation
        /// </summary>
        public ushort Flags
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the data that should be stored in the cache server.
        /// </summary>
        public byte[] Data
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether the operation is a read operation.
        /// </summary>
        /// <value>
        /// <c>true</c> if the operation is read kind; otherwise, <c>false</c>.
        /// </value>
        public bool IsReadKind
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is a online line command.
        /// I.e. the entire command is contained within the first line.
        /// </summary>
        public bool IsOneLine
        {
            get;
            set;
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            if (string.IsNullOrEmpty(Key))
            {
                throw new InvalidOperationException("Key has not been set, cannot continue");
            }

            if (!IsReadKind)
            {
                if (Flags > 0)
                {
                    return string.Concat(Name, Space, Key, Space, Convert.ToString(Flags), Space, Convert.ToString(Expiry.TotalSeconds), Space, Convert.ToString(Data.Length), "\r\n");
                }

                return string.Concat(Name, Space, Key, Space, Convert.ToString(Flags), Space, Convert.ToString(Expiry.TotalSeconds), Space, Convert.ToString(Data.Length), "\r\n");
            }

            return string.Concat(Name, Space, Key, "\r\n");
        }

        /// <summary>
        /// Appends the string representation of the operation as bytes to list of array segments
        /// Possible loss of non ascii key names, but should not be a problem since we pass the key through this method always
        /// </summary>
        /// <returns>A List of array segment containing bytes that represents the operation.</returns>
        public List<ArraySegment<byte>> ToPackageFormat()
        {
            List<ArraySegment<byte>> buffer = new List<ArraySegment<byte>> { new ArraySegment<byte>(Encoding.ASCII.GetBytes(ToString())) };

            if (!IsReadKind && !IsOneLine)
            {
                buffer.Add(new ArraySegment<byte>(Data));
                buffer.Add(new ArraySegment<byte>(CrLf));
            }

            return buffer;
        }
    }
}
